package com.snowfield.lb47.lbconfig.velocityService;

import com.snowfield.lb47.lbconfig.domain.*;
import com.snowfield.lbcommon.config.SnowFieldConfig;
import com.snowfield.lbcommon.domain.Cluster;
import com.snowfield.lbcommon.velocity.haproxy.*;
import com.snowfield.lbcommon.velocity.util.VelocityInitializer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class HAVelocityService {

    /**
     * 生成haproxy配置文件内容
     *
     * @param cluster
     * @param frontProxyList
     * @param proxyCrtMap
     * @param backServerCrtMap
     * @param httpCheckParamMap
     */
    public void createHaproxyConfigFile(Cluster cluster, List<FrontProxy> frontProxyList, HashMap<Long, String> proxyCrtMap,
                                        HashMap<String, BackserverCrt> backServerCrtMap, HashMap<Long, HttpCheckParam> httpCheckParamMap) {
        HaproxyConfig haproxyConfig = createHaproxyConfig(cluster, frontProxyList, proxyCrtMap, backServerCrtMap, httpCheckParamMap);

        VelocityInitializer.initVelocity();
        VelocityContext context = prepareContext(haproxyConfig);

        // 渲染模板
        StringWriter sw = new StringWriter();
        String template = "vm/haproxy.cfg.vm";
        Template tpl = Velocity.getTemplate(template, "UTF-8");
        tpl.merge(context, sw);
        try {
            String configFilePath = SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity.cfg";
//            String configFilePath = "/etc/haproxy/haproxy.cfg";
            FileUtils.write(new File(configFilePath), sw.toString(), Charset.forName("UTF-8"));
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /**
     * 生成用于检测的haproxy配置文件内容
     *
     * @param cluster
     * @param frontProxy        待检测的前端代理对象
     * @param proxyCrtMap
     * @param backServerCrtMap
     * @param httpCheckParamMap
     */
    public void createHaproxyConfigCheckFile(Cluster cluster, FrontProxy frontProxy, HashMap<Long, String> proxyCrtMap,
                                             HashMap<String, BackserverCrt> backServerCrtMap, HashMap<Long, HttpCheckParam> httpCheckParamMap) {
        Long proxyId = frontProxy.getId();
        List<FrontProxy> frontProxyList = new ArrayList<>();
        frontProxyList.add(frontProxy);
        HaproxyConfig haproxyConfig = createHaproxyConfig(cluster, frontProxyList, proxyCrtMap, backServerCrtMap, httpCheckParamMap);

        VelocityInitializer.initVelocity();
        VelocityContext context = prepareContext(haproxyConfig);

        // 渲染模板
        StringWriter sw = new StringWriter();
        String template = "vm/haproxy.cfg.vm";
        Template tpl = Velocity.getTemplate(template, "UTF-8");
        tpl.merge(context, sw);
        try {
            String configFilePath = SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity-" + proxyId + ".cfg";
//            String configFilePath = "/etc/haproxy/haproxy.cfg";
            FileUtils.write(new File(configFilePath), sw.toString(), Charset.forName("UTF-8"));
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /**
     * 生成haproxy配置文件对象数据
     */
    private HaproxyConfig createHaproxyConfig(Cluster cluster, List<FrontProxy> frontProxyList,
                                              HashMap<Long, String> proxyCrtMap, HashMap<String, BackserverCrt> backServerCrtMap,
                                              HashMap<Long, HttpCheckParam> httpCheckParamMap) {
        HaproxyConfig haproxyConfig = new HaproxyConfig();

        haproxyConfig.setGlobalSec(createHaproxyConfigGlobalSection(cluster));
        haproxyConfig.setDefaultSec(createHaproxyConfigDefaultSection());
        List<ListenSection> listenSectionList = createHaproxyConfigListenSection(frontProxyList, proxyCrtMap, backServerCrtMap, httpCheckParamMap);
        haproxyConfig.setListenSectionList(listenSectionList);

        for (ListenSection item : listenSectionList) {
            if (item.getAntibot()) {
                haproxyConfig.setAntibotBool(Boolean.TRUE);
                break;
            }
        }

        return haproxyConfig;
    }

    /**
     * 生成haproxy配置文件global域
     */
    private GlobalSection createHaproxyConfigGlobalSection(Cluster cluster) {
        //global 域
        GlobalSection global = new GlobalSection();

        String deviceIp = cluster.getDeviceIp();
        if (StringUtils.isNotEmpty(deviceIp)) {
            String localPeerName = deviceIp.substring(deviceIp.lastIndexOf(".") + 1);
            global.setLocalPeerName("haproxy" + localPeerName);
        }

        //支持400W连接
        global.setMaxconn(4000000);

        return global;
    }

    /**
     * 生成haproxy配置文件default域
     */
    private DefaultSection createHaproxyConfigDefaultSection() {
        //default 域
        DefaultSection def = new DefaultSection();
        def.setMode("tcp");
        def.setOptForwardfor(Boolean.TRUE);
        def.setOptRedispatch(Boolean.TRUE);
        def.setTimeoutClient("20s");
        def.setTimeoutConnect("20s");
        def.setTimeoutHttpKeepAlive("20s");
        def.setTimeoutHttpRequest("20s");
        def.setTimeoutServer("20s");
        //默认100W个连接
        def.setMaxconn(1000000);

        return def;
    }

    /**
     * 生成haproxy配置文件Listen域
     */
    private List<ListenSection> createHaproxyConfigListenSection(List<FrontProxy> frontProxyList, HashMap<Long, String> proxyCrtMap,
                                                                 HashMap<String, BackserverCrt> backServerCrtMap, HashMap<Long, HttpCheckParam> httpCheckParamMap) {
        List<ListenSection> listenSectionList = new ArrayList<>();
        for (FrontProxy frontProxy : frontProxyList) {

            ListenSection listenSection = new ListenSection();
            //basic info
            listenSection.setProxyName("proxy" + frontProxy.getId());
            String bindIp = frontProxy.getBindIp();
            if (bindIp.contains(":")) {
                //地址为v6时
                bindIp = "[" + bindIp + "]";
            }
            listenSection.setBind(bindIp + ":" + frontProxy.getBindPort());
            listenSection.setMode(frontProxy.getMode());
            listenSection.setMaxconn(frontProxy.getMaxconn());
            listenSection.setHealthCheck(frontProxy.getHealthCheck());

            //如果是http检测方式
            if (frontProxy.getHealthCheck().equals("httpservice")) {
                Long checkParamId = frontProxy.getCheckParamId();
                if (checkParamId != 0) {
                    /**
                     * 后台【自定义检测参数】功能未生效！如有需要请联系作者QQ:3961919621，
                     */
                }
            }

            //balance args
            listenSection.setBalance(frontProxy.getBalance());
            /**
             * 后台【策略参数配置】功能未生效！如有需要请联系作者QQ:3961919621，
             */

            //会话保持  1.cookie, 2.stick table
            if ("1".equals(frontProxy.getCookie())) {
                FECookie cookie = new FECookie();
                HashMap<String, Object> cookieMap = frontProxy.getCookieConfigDisplay();
                cookie.setName(String.valueOf(cookieMap.get("cookieName")));
                cookie.setInsert(Boolean.TRUE);
                cookie.setIndirect(Boolean.TRUE);
                cookie.setNocache("1".equals(cookieMap.get("nocache")));
                cookie.setHttponly("1".equals(cookieMap.get("httponly")));
                cookie.setSecure("1".equals(cookieMap.get("secure")));
                cookie.setMaxidle(cookieMap.get("maxidle") + "m");
                cookie.setMaxlife(cookieMap.get("maxlife") + "m");

                List<FECookie> cookieList = new ArrayList<>();
                cookieList.add(cookie);
                listenSection.setCookieList(cookieList);
            }
            listenSection.setStickTable("1".equals(frontProxy.getStickTable()));

            /**
             * ssl offloading
             */
            listenSection.setSslOffloading("1".equals(frontProxy.getSslOffloading()));
            if (listenSection.getSslOffloading()) {
                String proxyCrtPath = proxyCrtMap.get(frontProxy.getProxyCrtId());
                listenSection.setProxyCrtPath(proxyCrtPath);
            }
            listenSection.setRedirectHttps("1".equals(frontProxy.getHttp2https()));
            listenSection.setSslMinVer(frontProxy.getSslMinVer());
            listenSection.setSslMaxVer(frontProxy.getSslMaxVer());

            //http options
            listenSection.setOptForwardfor("1".equals(frontProxy.getForwardfor()));
            listenSection.setOptRedispatch("1".equals(frontProxy.getRedispatch()));
            listenSection.setOptKeepalive("1".equals(frontProxy.getKeepalive()));

            /**
             * backend server
             * server srv1 1.1.1.1:3389
             */
            List<BEServer> beServerList = new ArrayList<>();
            for (FrontProxyServer frontProxyServer : frontProxy.getFrontProxyServerList()) {
                BEServer beServer = new BEServer();

                beServer.setName("srv" + frontProxyServer.getServerId());
                beServer.setAddress(frontProxyServer.getServerIp());
                beServer.setServicePort(frontProxyServer.getServicePort());
                beServer.setCheckPort(frontProxyServer.getCheckPort());
                //默认要进行健康检测
                beServer.setCheck(Boolean.TRUE);
                beServer.setInter(frontProxyServer.getInter());
                beServer.setRise(frontProxyServer.getRise());
                beServer.setFall(frontProxyServer.getFall());
                beServer.setMaxconn(frontProxyServer.getMaxconn());
                beServer.setWeight(frontProxyServer.getWeight());
                beServer.setBackup("1".equals(frontProxyServer.getBackup()));

                if (StringUtils.isEmpty(frontProxyServer.getSrvCookie())) {
                    beServer.setCookieValue("srv" + frontProxyServer.getServerId());
                } else {
                    beServer.setCookieValue(frontProxyServer.getSrvCookie());
                }


                //设置证书信息
                String crtKey = frontProxyServer.getServerId() + ":" + frontProxyServer.getServicePort();
                BackserverCrt backserverCrt = backServerCrtMap.get(crtKey);
                if (backserverCrt != null) {
                    String crtPath = backserverCrt.getCrtPath();
                    if (StringUtils.isNotEmpty(crtPath)) {
                        beServer.setCrtPath(crtPath);
                        beServer.setSni(backserverCrt.getSni());
                        beServer.setSslMinVer(backserverCrt.getSslMinVer());
                        beServer.setSslMaxVer(backserverCrt.getSslMaxVer());
                    }
                }

                beServerList.add(beServer);
            }
            listenSection.setServerList(beServerList);

            /**
             * acl
             */
            List<FEAcl> aclList = new ArrayList<>();
            for (FrontProxyAcl frontProxyAcl : frontProxy.getFrontProxyAclList()) {
                FEAcl feAcl = new FEAcl();
                feAcl.setAclIf(frontProxyAcl.getAclIf());
                feAcl.setAclIfValue(frontProxyAcl.getAclIfValue());
                feAcl.setAclThen(frontProxyAcl.getAclThen());
                feAcl.setAclThenValue(frontProxyAcl.getAclThenValue());

                aclList.add(feAcl);
            }
            listenSection.setAclList(aclList);

            /**
             * header
             */
            List<FEHeader> headerList = new ArrayList<>();
            for (FrontProxyHeader frontProxyHeader : frontProxy.getFrontProxyHeaderList()) {
                FEHeader feHeader = new FEHeader();
                feHeader.setHeaderAction(frontProxyHeader.getHeaderAction());
                feHeader.setHeaderMethod(frontProxyHeader.getHeaderMethod());
                feHeader.setHeaderName(frontProxyHeader.getHeaderName());
                feHeader.setHeaderValue(frontProxyHeader.getHeaderValue());

                headerList.add(feHeader);
            }
            listenSection.setHeaderList(headerList);


            //web加速
            listenSection.setCompression("1".equals(frontProxy.getCompression()));
            listenSection.setCache("1".equals(frontProxy.getCache()));

            // http basic auth
            listenSection.setHttpauth("1".equals(frontProxy.getHttpauth()));
            if (listenSection.getHttpauth()) {
                listenSection.setHttpauthPath(frontProxy.getHttpauthPath().replaceAll(",", " "));
                listenSection.setHttpauthUsername(frontProxy.getHttpauthUsername());
                listenSection.setHttpauthPasswd(frontProxy.getHttpauthPasswd());
                listenSection.setHttpauthEnpasswd(frontProxy.getHttpauthEnpasswd());
            }

            //安全防护
            listenSection.setSlowAttack("1".equals(frontProxy.getShowAttack()));
            listenSection.setDdos("1".equals(frontProxy.getDdos()));
            listenSection.setAntibot("1".equals(frontProxy.getAntibot()));
            if ("black".equals(frontProxy.getBlackwhiteType())) {
                listenSection.setBlackListId(frontProxy.getBlackListId());
                listenSection.setWhiteListId(0L);
            } else if ("white".equals(frontProxy.getBlackwhiteType())) {
                listenSection.setBlackListId(0L);
                listenSection.setWhiteListId(frontProxy.getWhiteListId());
            } else {
                listenSection.setBlackListId(0L);
                listenSection.setWhiteListId(0L);
            }


            listenSectionList.add(listenSection);
        }

        return listenSectionList;
    }

    /**
     * 设置模板变量信息
     *
     * @return 模板列表
     */
    private static VelocityContext prepareContext(HaproxyConfig haproxyConfig) {
        VelocityContext velocityContext = new VelocityContext();

        velocityContext.put("global", haproxyConfig.getGlobalSec());
        velocityContext.put("default", haproxyConfig.getDefaultSec());
        velocityContext.put("listens", haproxyConfig.getListenSectionList());
        velocityContext.put("antibotBool", haproxyConfig.getAntibotBool());

        return velocityContext;
    }

    /**
     * 备份
     */
    public static void backupHAConfigFile() {
        File originFile = new File(SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity.cfg");
        File backupFile = new File(SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity.cfg.bak");
        if (originFile.exists()) {
            try {
                FileUtils.copyFile(originFile, backupFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 恢复
     */
    public static void restoreHAConfigFile() {
        File originFile = new File(SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity.cfg");
        File backupFile = new File(SnowFieldConfig.getHaConfigPath() + "/haproxy-velocity.cfg.bak");
        if (backupFile.exists()) {
            try {
                FileUtils.copyFile(backupFile, originFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
